package common

import (
	"common/configx"
	"common/zapx"
	"crypto/md5"
	r "crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/golang-jwt/jwt/v5"
	"go.uber.org/zap"
	"math/rand"
	"net"
	"strconv"
	"time"
)

// GetOutboundIP 获取本机的出口IP
func GetOutboundIP() (net.IP, error) {
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		return nil, err
	}
	defer conn.Close()
	localAddr := conn.LocalAddr().(*net.UDPAddr)
	return localAddr.IP, nil
}

// Md5FileByBytes 计算byte数据的md5值
func Md5FileByBytes(data []byte) string {
	m := md5.New()
	_, err := m.Write(data)
	if err != nil {
		zapx.Logger.Error("计算byte数据md5值失败", zap.Error(err))
		return ""
	}
	return hex.EncodeToString(m.Sum(nil))
}

// RandomInt 生成随机整数
func RandomInt(len int64) int64 {
	rr := rand.New(rand.NewSource(time.Now().Unix()))
	return rr.Int63n(len)
}

// ConsulSerUrl consul服务链接
func ConsulSerUrl(serName string) string {
	return fmt.Sprintf("consul://%s:%d/%s?healthy=true", configx.C.Consul.Address, configx.C.Consul.Port, serName)
}

// RandomStr  生成随机字符串
func RandomStr(length int) string {
	// 创建一个字节切片用于存储随机数据
	byteSlice := make([]byte, length)

	// 使用加密安全的随机数填充字节切片
	r.Read(byteSlice)
	// 使用base64编码将字节切片转换为字符串
	return base64.URLEncoding.EncodeToString(byteSlice)
}

// CheckJwtToken 检查并返回 token 信息
func CheckJwtToken(token string) (*jwt.RegisteredClaims, error) {
	parse, err := jwt.ParseWithClaims(token, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(configx.C.Jwt.Secret), nil
	})
	if err != nil {
		return nil, err
	}
	claims, ok := parse.Claims.(*jwt.RegisteredClaims)
	if !ok {
		return nil, errors.New("解析 token 失败")
	}
	//检查token 是否过期
	if !claims.ExpiresAt.After(time.Now()) {
		return nil, errors.New("token 已过期")
	}
	return claims, nil
}

// GenerateJwtToken 生成 jwt token
func GenerateJwtToken(userId int32) (string, error) {
	claims := jwt.RegisteredClaims{
		Issuer:    "易漫网",
		Subject:   "用户登录令牌",
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(configx.C.Jwt.ExpiredHours))),
		IssuedAt:  jwt.NewNumericDate(time.Now()),
		ID:        strconv.Itoa(int(userId)),
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(configx.C.Jwt.Secret))
}
